#ifndef V_THREAD_HANDLER_H
#define V_THREAD_HANDLER_H

#include <chrono>
#include <map>
#include <mutex>
#include <vector>
#include <thread>
#include <condition_variable>
#include <algorithm>
#include <iostream>

#include "VThreadMessage.h"

/*
 * VThreadHandler will run in it's own thread, you don't want to care about it.
 * VThreadMessage will be proccess by the VThreadHandler. Two ways to add your task to the VThreadHandler.
 * 1. send message to the handler
 * 2. post the task(Function) to handler
 */

namespace VUtils {
    class VThreadHandler {
    public:
        VThreadHandler() : stop(false), stopWhenEmpty(false) {
            looper = std::thread(
                    [this]() {
                        for (;;) {

                            if (this->stopWhenEmpty && this->msg_Q.empty()) {
                                break;
                            }

                            if (stop) {
                                msg_Q.clear();
                                break;
                            }

                            VThreadMessage msg;
                            { // 必须加大括号，unique_lock在这个括号内有效，括号外会被析构
                                std::unique_lock<std::mutex> lock(this->queue_mutex);
                                if (this->msg_Q.empty()) {
                                    this->condition.wait(lock, [this] {
                                        return this->stop || this->stopWhenEmpty || !this->msg_Q.empty();
                                    });
                                    continue;
                                }

                                // 等待到消息执行时
                                auto t = this->msg_Q.back().when - std::chrono::system_clock::now();
                                if (t > std::chrono::milliseconds(0)) {
                                    this->condition.wait_for(lock, t, [this] {
//                            return (this->msg_Q.back().when > std::chrono::system_clock::now())
//                                   || this->stop || this->stopWhenEmpty || !this->msg_Q.empty();
                                        return true;
                                    });
                                    continue;
                                }

                                msg = msg_Q.back();
                                msg_Q.pop_back();
                            }
                            this->dispatchMessage(msg);
                        }
                    });
        }

        ~VThreadHandler() {
            {
                std::unique_lock<std::mutex> lock(queue_mutex);
                stop = true;
            }
            condition.notify_all();
            looper.join();
            msg_Q.clear();

        }

        void handleMessage(VThreadMessage &msg) {
        }

        bool sendMessageAtTime(VThreadMessage &msg, long uptimeMillis) {
            msg.setWhen(uptimeMillis);

            std::unique_lock<std::mutex> lock(queue_mutex);
            auto i = std::find(msg_Q.begin(), msg_Q.end(), msg);
            msg_Q.erase(i);

            msg_Q.push_back(msg);
            std::sort(msg_Q.begin(), msg_Q.end(), std::greater<VThreadMessage>());
            condition.notify_one();
            return true;
        }

        bool sendMessage(VThreadMessage &msg) {
            std::unique_lock<std::mutex> lock(queue_mutex);
            auto i = find(msg_Q.begin(), msg_Q.end(), msg);
            if (i != msg_Q.end())
                msg_Q.erase(i);

            msg_Q.push_back(msg);
            std::sort(msg_Q.begin(), msg_Q.end(), std::greater<VThreadMessage>());
            condition.notify_one();
            return true;
        }

        bool sendEmptyMessage(int what) {
            return sendEmptyMessage(what, 0);
        }

        bool sendEmptyMessage(int what, long uptimeMillis) {

            if (what < 0)
                return false;

            VThreadMessage msg(what);
            msg.setWhen(uptimeMillis);

            std::unique_lock<std::mutex> lock(queue_mutex);

            auto i = find(msg_Q.begin(), msg_Q.end(), msg);
            if (i != msg_Q.end()) {
                msg_Q.erase(i);
            }

            msg_Q.push_back(msg);
//	std::sort(msg_Q.begin(), msg_Q.end(),ValComp<VThreadMessage>());
            // 跟进时间进行降序排列
            std::sort(msg_Q.begin(), msg_Q.end(), std::greater<VThreadMessage>());

            condition.notify_one();
            return true;
        }

        bool post(VThreadMessage::Function f) {
            return postAtTime(f, 0);
        }

        bool postAtTime(VThreadMessage::Function f, long uptimeMillis) {

            if (f == nullptr) {
                return false;
            }

            std::unique_lock<std::mutex> lock(queue_mutex);
            VThreadMessage msg;
            msg.setWhen(uptimeMillis);
            msg.setFunction(f);
            msg_Q.push_back(msg);
            std::sort(msg_Q.begin(), msg_Q.end(), std::greater<VThreadMessage>());
            condition.notify_one();
            return true;
        }

        void removeMessages(int what) {
            if (what < 0)
                return;

            std::unique_lock<std::mutex> lock(queue_mutex);

            auto i = find(msg_Q.begin(), msg_Q.end(), what);
            if (i != msg_Q.end()) {
                msg_Q.erase(i);
            }

            condition.notify_one();
        }

        void removeCallbackAndMessages() {
            std::unique_lock<std::mutex> lock(queue_mutex);
            msg_Q.clear();
        }

        void stopSafty(bool stopSafty) {
            std::unique_lock<std::mutex> lock(queue_mutex);
            if (stopSafty) {
                stopWhenEmpty = true;
            } else {
                stop = true;
            }
        }


        bool isQuiting() {
            std::unique_lock<std::mutex> lock(queue_mutex);
            return stop || stopWhenEmpty;
        }

        void dispatchMessage(VThreadMessage &msg) {
            if (msg.task != nullptr) {
                msg.task();
            } else {
                if (msg.m_what < 0)
                    return;
                handleMessage(msg);
            }
        }

        /*
         * for msgQueue sorted when insert,
         * ascending order
         */
        template<class T>
        class ValComp {
        public:
            bool operator()(const T &t1, const T &t2) const {
                return (t1 < t2);
            }
        };

    private:
        std::vector<VThreadMessage> msg_Q;

        std::mutex queue_mutex;
        std::condition_variable condition;
        std::thread looper;
        bool stop;
        bool stopWhenEmpty;
    };
}
#endif
